<!--
 * @Author       : Hejh(3010733382@qq.com)
 * @Version      : V1.0
 * @Date         : 2024-04-13 15:02:37
 * @Description  : 用于网盘列表视图
-->

<template>
  <div class="grid-con" ref="gridConRef">
    <div
      class="source-list flex flex-wrap p-2 pl-1 overflow-auto mb-2"
      :style="{ 'max-height': maxHeight }"
      @mousedown="handleMouseDown"
      @mousemove="handleMouseMove"
      @mouseup="handleMouseUp"
    >
      <div
        ref="selectionBox"
        v-if="dragInfo.isDragging"
        :style="selectionStyle"
        class="selection-box"
      ></div>
      <div
        :draggable="props.draggleable"
        class="source-item flex flex-col items-center"
        :class="{
          active: checkedRowKeys.some((v) => v.id === item.id),
          hover: item.isHover === true
        }"
        v-for="(item, index) in data"
        :key="item.id"
        @contextmenu="(e: MouseEvent) => handleContextMenu(e, item)"
        @click="(e: MouseEvent) => onClick(e, item, index)"
        @dblclick="(e: MouseEvent) => ondblclick(e, item)"
        @mouseover="(e: MouseEvent) => onmouseover(e, item)"
        @mouseleave="(e: MouseEvent) => onmouseleave(e, item)"
        @mouseup="onMouseUp"
        @dragover="(e: DragEvent) => handleDragOver(e, item)"
        @drop="(e: DragEvent) => handleDrop(e, item)"
        @dragstart="(e: DragEvent) => handleDragStart(e, item)"
        @dragend="(e: DragEvent) => handleDragEnd(e, item)"
        @dragleave="(e: DragEvent) => (item.isHover = false)"
      >
        <div
          class="absolute left-1 top-1 cursor-pointer"
          @click="(e: MouseEvent) => handleIconSelect(e, item)"
        >
          <i class="iconfont icon-ziyuan text-[20px] select-icon"></i>
        </div>

        <div
          class="absolute right-5 top-10 cursor-pointer z-10"
          v-if="item.Share && item.Share.isShareTo"
        >
          <SvgIcon icon="fenxiang-copy" size="16px"></SvgIcon>
        </div>

        <div
          class="absolute right-5 top-14 cursor-pointer z-10 bg-[#fff]"
          v-if="item.Share && item.Share.isLink"
        >
          <SvgIcon icon="zhuanfa2-copy" size="16px"></SvgIcon>
        </div>
        <div class="icon mt-3">
          <SvgIcon
            :icon="
              item.isDir
                ? getFileTypeInfoByType(item.type).myIcon
                : getFileTypeBySuffix(item.filename).myIcon
            "
            size="50px"
          ></SvgIcon>
        </div>
        <ShowOrEdit
          :draggleable="false"
          class="mt-1"
          :value="item.filename"
          :is-edit="item.isEdit"
          @edit-change="(value: boolean) => onEditChange(value, item)"
          @update:value="(value: string | number) => onUpdateValue(value, item)"
          @on-finish="(initValue: string | number) => onFinish(initValue, item)"
        ></ShowOrEdit>
        <div class="desc">{{ renderDetail(item) }}</div>
      </div>
    </div>

    <n-pagination
      class="float-right"
      :label="pagination.label"
      :page="pagination.page"
      :page-size="pagination.pageSize"
      :size="pagination.size"
      :page-slot="pagination.pageSlot"
      :show-size-picker="pagination.showSizePicker"
      :suffix="pagination.suffix"
      :page-sizes="pagination.pageSizes"
      :item-count="pagination.itemCount"
      @update:page="pagination.onChange"
      @update:page-size="pagination.onUpdatePageSize"
    />
  </div>
</template>

<script setup lang="ts">
import { useMessage } from 'naive-ui'
import type { PaginationProps } from 'naive-ui'
import useExplorerStore from '@/stores/modules/explorer'
import type { IndexObj } from '@/types/commom'
import type { SourceRowData } from '@/types/source'
import { convertFromBytes, throllte } from '@/utils'
import {
  getFileTypeInfoByType,
  getFileTypeBySuffix
} from '@/utils/commonBusiness'
import ShowOrEdit from './ShowOrEdit.vue'
import { navTypeList } from '@/constants/explorer'
import { load } from '@/utils/loading'
import { moveSource } from '@/api/source'

// onUnmounted(() => {

// })

const message = useMessage()
const explorerStore = useExplorerStore()

const props = withDefaults(
  defineProps<{
    data: any[] // 传入的data数据
    checkedRowKeys: any[] // 选中的行
    pages: number
    total: number
    pageSize?: number
    onContextmenu?: any
    maxHeight?: any
    draggleable?: boolean
    renameable?: boolean
  }>(),
  {
    pageSize: 500,
    maxHeight: 400,
    onContextmenu: (row: IndexObj) => {
      return () => {}
    },
    draggleable: true,
    renameable: true
  }
)

const emits = defineEmits<{
  (e: 'pageChange', page?: number, pageSize?: number): void
  (e: 'selectChange', selectKeys: any[], selectRows: any[]): void
  (e: 'itemClick', clickRow: any): void
  (e: 'itemDbClick', clickRow: any): void
  (e: 'mouseover', row: any, event: MouseEvent): void
  (e: 'mouseleave', row: any, event: MouseEvent): void
  (e: 'create-finish', initValue: any, row: any): void
  (e: 'reflesh'): void
}>()

const handleDragOver = (event: DragEvent, item: any) => {
  if (item.isDir && !props.checkedRowKeys.some((v) => v.id === item.id)) {
    item.isHover = true
    // 阻止默认行为以允许放置
    event.preventDefault()
    event.stopPropagation()
  }
}

const handleDrop = (event: DragEvent, item: any) => {
  event.stopPropagation()
  event.preventDefault()
  item.isHover = false
  console.log(item, props.checkedRowKeys)
  const finalSources = props.checkedRowKeys
  if (!finalSources.length) {
    return message.warning('请选择文件')
  }
  // 回收站不支持此类操作
  if (explorerStore.curNav!.myType === navTypeList.USER_RECYCLE) {
    return message.warning('回收站不支持剪切操作')
  }
  load.show()
  moveSource({
    parentId: item.id,
    sourceIds: props.checkedRowKeys.map((item) => item.id)
  })
    .then(() => {
      message.success('文件移动成功')
      emits('reflesh')
    })
    .catch(console.error)
    .finally(() => {
      load.hide()
    })
}

const handleDragStart = (event: DragEvent, item: any) => {
  // ;(event.target as HTMLElement).style.opacity = '0.5'
  console.log('drag start', event)
  // event.dataTransfe
}

const handleDragEnd = (event: DragEvent, item: any) => {
  console.log('drag end', event)
}

// 分页查询后同步到分页器
watch(
  () => props.pages,
  (newValue) => {
    pagination.pageCount = newValue
  },
  {
    deep: true
  }
)

watch(
  () => props.total,
  (newValue) => {
    // console.log('222')
    dragInfo.isDragging = false

    pagination.itemCount = newValue
  },
  {
    deep: true
  }
)

const pagination = reactive<PaginationProps>({
  page: 1,
  pageSize: props.pageSize || 50,
  size: 'small',
  pageCount: props.pages,
  itemCount: props.total,
  pageSlot: 7,
  showSizePicker: true,
  label: (info) => {
    return h(
      NButton,
      {
        type: info.active ? 'primary' : 'tertiary',
        secondary: !info.active,
        style: {
          width: '100%',
          height: '100%'
        }
      },
      { default: () => info.node }
    )
  },
  suffix({ itemCount }) {
    return `${itemCount}条记录`
  },
  pageSizes: [
    {
      label: '10条/每页',
      value: 10
    },
    {
      label: '50条/每页',
      value: 50
    },
    {
      label: '100条/每页',
      value: 100
    },
    {
      label: '200条/每页',
      value: 200
    },
    {
      label: '500条/每页',
      value: 500
    },
    {
      label: '1000条/每页',
      value: 1000
    },
    {
      label: '2000条/每页',
      value: 2000
    }
  ],
  onChange: (page: number) => {
    pagination.page = page
    emits('pageChange', page, undefined)
  },
  onUpdatePageSize: (pageSize: number) => {
    pagination.pageSize = pageSize
    pagination.page = 1
    emits('pageChange', 1, pageSize)
  }
})

// 渲染文件详情
const renderDetail = (row: SourceRowData) => {
  if (!row.isDir) {
    return row.file ? convertFromBytes(row.file.size) : '0B'
  }
  return `${row.hasFile + row.hasFolder}项 ${
    row.file ? convertFromBytes(row.file.size) : '0B'
  }`
}

// 表格重命名区域
const onEditChange = (v: boolean, row: SourceRowData) => {
  if (row.deleteFlag || !props.renameable) {
    row.isEdit = false
    return
  }
  row.isEdit = v
}
const onUpdateValue = (v: any, row: SourceRowData) => {
  row.filename = v
  // 进行重命名
}
const onFinish = (initValue: any, row: SourceRowData) => {
  emits('create-finish', initValue, row)
}

const handleIconSelect = (e: MouseEvent, row: any) => {
  e.preventDefault()
  e.stopPropagation()
  emits('itemClick', row)
}
const onClick = (e: MouseEvent, row: any, index: number) => {
  e.preventDefault()
  e.stopPropagation()
  if (e.shiftKey) {
    let firstIndex = 0
    if (props.checkedRowKeys.length > 0) {
      const index = props.data.findIndex(
        (item) => item.id === props.checkedRowKeys[0].id
      )
      firstIndex = index === -1 ? 0 : index
    }
    const lastIndex = index
    const minIndex = Math.min(firstIndex, lastIndex)
    const maxIndex = Math.max(firstIndex, lastIndex)

    // console.log(minIndex, maxIndex)

    const selectRows = props.data.slice(minIndex, maxIndex + 1)
    emits(
      'selectChange',
      selectRows.map((item) => item.id),
      selectRows
    )
    return
  } else if (e.ctrlKey) {
    emits('itemClick', row)
    return
  }
  emits('selectChange', [row.id], [row])
}

const onMouseDown2 = (e: MouseEvent, row: any, index: number) => {
  emits('selectChange', [row.id], [row])
}

const ondblclick = (e: MouseEvent, row: any) => {
  e.preventDefault()
  e.stopPropagation()
  emits('itemDbClick', row)
}

const onmouseover = (e: MouseEvent, row: any) => {
  emits('mouseover', row, e)
}
const onmouseleave = (e: MouseEvent, row: any) => {
  emits('mouseleave', row, e)
}

const onMouseUp = (e: MouseEvent) => {
  const container = gridConRef.value.querySelector('.source-list')
  const top = container.scrollTop

  requestAnimationFrame(() => {
    container.scrollTop = top + 0
  })
}

const handleContextMenu = (e: MouseEvent, row: any) => {
  return props.onContextmenu(row)(e)
}

// ======================================= 框选的功能 ===================================

const dragInfo = reactive({
  isDragging: false,
  startX: 0,
  startY: 0,
  endX: 0,
  endY: 0
})

const selectionStyle = computed<any>(() => {
  return {
    position: 'absolute',
    left: `${Math.min(dragInfo.startX, dragInfo.endX)}px`,
    top: `${Math.min(dragInfo.startY, dragInfo.endY)}px`,
    width: `${Math.abs(dragInfo.endX - dragInfo.startX)}px`,
    height: `${Math.abs(dragInfo.endY - dragInfo.startY)}px`,
    border: '1px solid blue',
    pointerEvents: 'none',
    background: 'rgba(0,0,255,0.05)'
  }
})

const selectionBox = ref<any>()
const gridConRef = ref<any>()

// 卡片框选
const updateSelectedCards = () => {
  const selectedCards: any[] = []
  const selectionRect = selectionBox.value.getBoundingClientRect()
  props.data.forEach((card, index) => {
    const cardRect = gridConRef.value
      .querySelectorAll('.source-item')
      [index].getBoundingClientRect()
    if (
      cardRect.left < selectionRect.right &&
      cardRect.right > selectionRect.left &&
      cardRect.top < selectionRect.bottom &&
      cardRect.bottom > selectionRect.top
    ) {
      selectedCards.push(card)
    }
  })

  emits(
    'selectChange',
    selectedCards.map((item) => item.id),
    selectedCards
  )
}

const handleMouseDown = (event: MouseEvent) => {
  dragInfo.isDragging = false
  const cls = (event.target as any).className
  if (
    event.button === 0 &&
    typeof cls === 'string' &&
    cls.includes('source-list')
  ) {
    // console.log('mouse down')
    event.preventDefault()
    event.stopPropagation()
    props.data.forEach((item) => (item.isEdit = false))
    emits('selectChange', [], [])
    dragInfo.isDragging = true
    const container = gridConRef.value.querySelector('.source-list')
    const rect = container!.getBoundingClientRect()
    dragInfo.startX = event.clientX - rect!.left
    dragInfo.startY = event.clientY - rect!.top + container!.scrollTop
    dragInfo.endX = event.clientX - rect!.left
    dragInfo.endY = event.clientY - rect!.top + container!.scrollTop
  }
}
const handleMouseMove = throllte((event: MouseEvent) => {
  if (dragInfo.isDragging) {
    // console.log('mouse move')

    event.stopPropagation()
    event.preventDefault()
    const container = gridConRef.value.querySelector('.source-list')
    const containerRect = container!.getBoundingClientRect()
    if (event.clientY < containerRect.top + 30) {
      container!.scrollTop -= 20
    } else if (event.clientY > containerRect.bottom - 30) {
      container!.scrollTop += 20
    }
    updateSelectedCards()
    dragInfo.endX = event.clientX - containerRect!.left
    dragInfo.endY = event.clientY - containerRect!.top + container!.scrollTop
  }
}, 4)

const handleMouseUp = (event: MouseEvent) => {
  event.stopPropagation()
  event.preventDefault()
  dragInfo.isDragging = false
  // if (dragInfo.isDragging) {
  //   // console.log('mouse up')

  //   dragInfo.isDragging = false
  // }
}
</script>

<style scoped lang="scss">
.grid-con {
  position: relative;
  .selection-box {
    z-index: 100;
  }
  .source-list {
    position: relative;
    .source-item {
      flex-shrink: 0;
      position: relative;
      width: 100px;
      max-width: 100px;
      height: 150px;
      max-height: 150px;
      padding: 5px;
      margin-bottom: 10px;
      margin-left: 10px;
      border: 1px solid transparent;
      user-select: none;
      .select-icon {
        transition: all 0.16s;
        display: none;
      }

      &:hover {
        background: #e5f3ff;
        border-color: transparent;

        .select-icon {
          display: block;
          color: #ccc;
        }
      }

      &.active {
        background: #cce8ff;
        border-color: #a8d8ff;
        .select-icon {
          display: block;
          color: #3b8cff;
        }
      }

      &.hover {
        background: #cce8ff;
        border-color: #a8d8ff;
      }
    }
  }

  :deep(.n-pagination .n-pagination-item) {
    padding: 0 !important;
    border: 0;
    width: 25px !important;
    height: 25px !important;
    min-width: 25px;

    & .n-button {
      height: 100%;
      width: 100%;
      min-width: 25px;
      min-height: 25px;
      padding: 0;
      margin: 0;
    }
  }
}
</style>
